﻿#include "Terrain.h"
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <random>

Terrain::Terrain()
    : m_width(0)
    , m_height(0)
    , m_vertexCount(0)
    , m_indexCount(0)
    , m_pVertexBuffer(NULL)
    , m_pIndexBuffer(NULL)
    , m_pTextureGrass(NULL)
    , m_pTextureDirt(NULL)
    , m_pTextureRock(NULL)
    , m_pTextureWater(NULL)
{
    m_layerIndexCounts.fill(0);
    m_layerIndexBuffers.fill(NULL);
}

Terrain::~Terrain()
{
    Cleanup();
}

bool Terrain::Initialize(LPDIRECT3DDEVICE9 device, int width, int height)
{
    m_width = width;
    m_height = height;
    m_vertexCount = width * height;
    m_indexCount = (width - 1) * (height - 1) * 6;

    GenerateHeightMap();

    m_vertices.resize(m_vertexCount);
    for (int z = 0; z < m_height; ++z)
    {
        for (int x = 0; x < m_width; ++x)
        {
            int index = z * m_width + x;
            m_vertices[index].position = D3DXVECTOR3(
                static_cast<float>(x),
                m_heightMap[index],
                static_cast<float>(z));
            m_vertices[index].normal = D3DXVECTOR3(0.0f, 1.0f, 0.0f);
        }
    }

    CalculateNormals();
    GenerateTextureCoords();

    m_indices.resize(m_indexCount);
    int offset = 0;
    for (int z = 0; z < m_height - 1; ++z)
    {
        for (int x = 0; x < m_width - 1; ++x)
        {
            int topLeft = z * m_width + x;
            int topRight = topLeft + 1;
            int bottomLeft = (z + 1) * m_width + x;
            int bottomRight = bottomLeft + 1;

            m_indices[offset++] = topLeft;
            m_indices[offset++] = topRight;
            m_indices[offset++] = bottomLeft;

            m_indices[offset++] = bottomLeft;
            m_indices[offset++] = topRight;
            m_indices[offset++] = bottomRight;
        }
    }

    if (!CreateBuffers(device))
        return false;

    if (!LoadTextures(device))
        return false;

    
    BuildLayerIndexData();

    if (!CreateLayerIndexBuffers(device))
        return false;

    return true;
}

void Terrain::GenerateHeightMap()
{
    m_heightMap.resize(m_vertexCount);

    std::random_device rd;
    std::mt19937 gen(rd());
    std::uniform_real_distribution<float> dis(0.0f, 1.0f);

    // 瀹氫箟3涓柟鍧楀爢灞辫剦鐨勪腑蹇冧綅缃?
    struct BlockMountain {
        int centerX, centerZ;
        int radius;
    };
    BlockMountain mountains[3] = {
        {200, 200, 50},   // 宸︿笂瑙?
        {800, 200, 50},   // 鍙充笂瑙?
        {500, 800, 60}    // 涓嬫柟涓棿
    };

    // 瀹氫箟姘村潙鍖哄煙锛堝乏涓嬭锛?
    int waterCenterX = 200;
    int waterCenterZ = 800;
    int waterRadius = 80;

    for (int z = 0; z < m_height; ++z)
    {
        for (int x = 0; x < m_width; ++x)
        {
            int index = z * m_width + x;
            float height = 10.0f;  // 鍩虹骞冲師楂樺害

            // 骞冲師涓橀櫟鍦拌矊锛堝钩婊戞尝鍔級
            float hillPattern = sinf(x * 0.02f) * cosf(z * 0.02f) * 3.0f;
            height += hillPattern;

            // 鏂瑰潡鍫嗗北鑴夛紙3涓綅缃級
            for (int i = 0; i < 3; i++)
            {
                int dx = x - mountains[i].centerX;
                int dz = z - mountains[i].centerZ;
                float distance = sqrtf(dx * dx + dz * dz);

                if (distance < mountains[i].radius)
                {
                    // 鏂瑰潡鍫嗛鏍硷細闃舵寮忛珮搴?
                    int layers = (int)((mountains[i].radius - distance) / 10.0f);
                    if (layers > 0)
                    {
                        height += layers * 5.0f;  // 姣忓眰5鍗曚綅楂樺害
                    }
                }
            }

            // 姘村潙鍖哄煙锛堢暐浣庝簬鍦伴潰锛?
            int dx = x - waterCenterX;
            int dz = z - waterCenterZ;
            float distanceToWater = sqrtf(dx * dx + dz * dz);
            if (distanceToWater < waterRadius)
            {
                // 姘村潙涓績娣卞害涓哄熀纭€楂樺害-2锛岃竟缂樺钩婊戣繃娓?
                float depthFactor = (waterRadius - distanceToWater) / waterRadius;
                height = 10.0f - depthFactor * 2.0f;  // 鏈€澶氶檷浣?鍗曚綅
            }

            // 娣诲姞寰皬闅忔満鍣０
            height += dis(gen) * 0.5f;

            // 纭繚楂樺害涓烘鏁?
            if (height < 5.0f)
                height = 5.0f;

            m_heightMap[index] = height;
        }
    }
}

void Terrain::CalculateNormals()
{
    for (int i = 0; i < m_vertexCount; ++i)
    {
        m_vertices[i].normal = D3DXVECTOR3(0.0f, 0.0f, 0.0f);
    }

    for (int z = 0; z < m_height - 1; ++z)
    {
        for (int x = 0; x < m_width - 1; ++x)
        {
            int topLeft = z * m_width + x;
            int topRight = topLeft + 1;
            int bottomLeft = (z + 1) * m_width + x;
            int bottomRight = bottomLeft + 1;

            const D3DXVECTOR3& v0 = m_vertices[topLeft].position;
            const D3DXVECTOR3& v1 = m_vertices[topRight].position;
            const D3DXVECTOR3& v2 = m_vertices[bottomLeft].position;
            const D3DXVECTOR3& v3 = m_vertices[bottomRight].position;

            D3DXVECTOR3 edge1 = v1 - v0;
            D3DXVECTOR3 edge2 = v2 - v0;
            D3DXVECTOR3 normal;
            D3DXVec3Cross(&normal, &edge1, &edge2);
            D3DXVec3Normalize(&normal, &normal);

            m_vertices[topLeft].normal += normal;
            m_vertices[topRight].normal += normal;
            m_vertices[bottomLeft].normal += normal;

            edge1 = v2 - v1;
            edge2 = v3 - v1;
            D3DXVec3Cross(&normal, &edge1, &edge2);
            D3DXVec3Normalize(&normal, &normal);

            m_vertices[topRight].normal += normal;
            m_vertices[bottomLeft].normal += normal;
            m_vertices[bottomRight].normal += normal;
        }
    }

    for (int i = 0; i < m_vertexCount; ++i)
    {
        D3DXVec3Normalize(&m_vertices[i].normal, &m_vertices[i].normal);
    }
}

void Terrain::GenerateTextureCoords()
{
    const float texScale = 0.1f;

    for (int z = 0; z < m_height; ++z)
    {
        for (int x = 0; x < m_width; ++x)
        {
            int index = z * m_width + x;
            m_vertices[index].texCoord = D3DXVECTOR2(x * texScale, z * texScale);
        }
    }
}

bool Terrain::CreateBuffers(LPDIRECT3DDEVICE9 device)
{
    if (FAILED(device->CreateVertexBuffer(
        m_vertexCount * sizeof(TerrainVertex),
        0,
        TerrainVertex::FVF,
        D3DPOOL_MANAGED,
        &m_pVertexBuffer,
        NULL)))
    {
        return false;
    }

    void* pVertices = NULL;
    if (FAILED(m_pVertexBuffer->Lock(0, 0, &pVertices, 0)))
        return false;

    std::memcpy(pVertices, m_vertices.data(), m_vertexCount * sizeof(TerrainVertex));
    m_pVertexBuffer->Unlock();

    if (FAILED(device->CreateIndexBuffer(
        m_indexCount * sizeof(DWORD),
        0,
        D3DFMT_INDEX32,
        D3DPOOL_MANAGED,
        &m_pIndexBuffer,
        NULL)))
    {
        return false;
    }

    void* pIndices = NULL;
    if (FAILED(m_pIndexBuffer->Lock(0, 0, &pIndices, 0)))
        return false;

    std::memcpy(pIndices, m_indices.data(), m_indexCount * sizeof(DWORD));
    m_pIndexBuffer->Unlock();

    return true;
}

bool Terrain::LoadTextures(LPDIRECT3DDEVICE9 device)
{
    HRESULT hr = D3DXCreateTexture(device, 64, 64, 1, 0, D3DFMT_A8R8G8B8,
        D3DPOOL_MANAGED, &m_pTextureGrass);
    if (FAILED(hr))
        return false;

    if (SUCCEEDED(hr))
    {
        D3DLOCKED_RECT rect;
        m_pTextureGrass->LockRect(0, &rect, NULL, 0);
        DWORD* texels = static_cast<DWORD*>(rect.pBits);

        // 娉ュ湡鑹插湴鏉跨汗鐞嗭紙瀹屽叏涓嶉€忔槑锛屾槑鏄剧殑妫曡壊鏂瑰潡璐村浘锛?
        for (int y = 0; y < 64; ++y)
        {
            for (int x = 0; x < 64; ++x)
            {
                int i = y * 64 + x;

                // 鍩虹棰滆壊锛氭繁妫曡壊娉ュ湡锛堥ケ鍜屽害楂橈紝瀹屽叏涓嶉€忔槑锛?
                int r = 120;
                int g = 80;
                int b = 40;

                // 16x16 鏂瑰潡缃戞牸锛圡inecraft 椋庢牸锛?
                bool isBorder = (x % 16 == 0 || y % 16 == 0);
                if (isBorder)
                {
                    // 娣辫壊杈规
                    r = 80;
                    g = 50;
                    b = 20;
                }

                // 娣诲姞绾圭悊缁嗚妭锛堟鐩樻牸浜ゆ浛锛?
                if (((x / 16) + (y / 16)) % 2 == 0)
                {
                    r += 20;
                    g += 15;
                    b += 10;
                }

                // 纭繚棰滆壊鍦ㄦ湁鏁堣寖鍥村唴
                if (r < 0) r = 0; if (r > 255) r = 255;
                if (g < 0) g = 0; if (g > 255) g = 255;
                if (b < 0) b = 0; if (b > 255) b = 255;

                texels[i] = D3DCOLOR_ARGB(255, r, g, b);  // 瀹屽叏涓嶉€忔槑 (Alpha=255)
            }
        }
        m_pTextureGrass->UnlockRect(0);
    }

    hr = D3DXCreateTexture(device, 64, 64, 1, 0, D3DFMT_A8R8G8B8,
        D3DPOOL_MANAGED, &m_pTextureDirt);
    if (FAILED(hr))
        return false;

    if (SUCCEEDED(hr))
    {
        D3DLOCKED_RECT rect;
        m_pTextureDirt->LockRect(0, &rect, NULL, 0);
        DWORD* texels = static_cast<DWORD*>(rect.pBits);
        for (int i = 0; i < 64 * 64; ++i)
        {
            texels[i] = D3DCOLOR_ARGB(255,
                100 + rand() % 50,
                70 + rand() % 30,
                40 + rand() % 20);
        }
        m_pTextureDirt->UnlockRect(0);
    }

    hr = D3DXCreateTexture(device, 64, 64, 1, 0, D3DFMT_A8R8G8B8,
        D3DPOOL_MANAGED, &m_pTextureRock);
    if (FAILED(hr))
        return false;

    if (SUCCEEDED(hr))
    {
        D3DLOCKED_RECT rect;
        m_pTextureRock->LockRect(0, &rect, NULL, 0);
        DWORD* texels = static_cast<DWORD*>(rect.pBits);
        for (int i = 0; i < 64 * 64; ++i)
        {
            int gray = 80 + rand() % 60;
            texels[i] = D3DCOLOR_ARGB(255, gray, gray, gray);
        }
        m_pTextureRock->UnlockRect(0);
    }

    // 鍒涘缓姘撮潰绾圭悊锛堣摑鑹插甫娉㈢汗锛?
    hr = D3DXCreateTexture(device, 64, 64, 1, 0, D3DFMT_A8R8G8B8,
        D3DPOOL_MANAGED, &m_pTextureWater);
    if (FAILED(hr))
        return false;

    if (SUCCEEDED(hr))
    {
        D3DLOCKED_RECT rect;
        m_pTextureWater->LockRect(0, &rect, NULL, 0);
        DWORD* texels = static_cast<DWORD*>(rect.pBits);
        for (int y = 0; y < 64; ++y)
        {
            for (int x = 0; x < 64; ++x)
            {
                // 钃濊壊姘撮潰甯﹁交寰尝绾规晥鏋?
                int wave = (int)(sinf(x * 0.3f) * cosf(y * 0.3f) * 20.0f);
                int r = 20 + wave;
                int g = 80 + wave;
                int b = 180 + wave;
                if (r < 0) r = 0; if (r > 255) r = 255;
                if (g < 0) g = 0; if (g > 255) g = 255;
                if (b < 0) b = 0; if (b > 255) b = 255;
                texels[y * 64 + x] = D3DCOLOR_ARGB(255, r, g, b);
            }
        }
        m_pTextureWater->UnlockRect(0);
    }

    return true;
}
void Terrain::BuildLayerIndexData()
{
    for (size_t i = 0; i < m_layerIndices.size(); ++i)
    {
        m_layerIndices[i].clear();
        m_layerIndexCounts[i] = 0;
    }

    for (int i = 0; i < m_indexCount; i += 3)
    {
        int index0 = m_indices[i];
        int index1 = m_indices[i + 1];
        int index2 = m_indices[i + 2];

        TerrainLayer layer = ClassifyTriangle(index0, index1, index2);
        m_layerIndices[layer].push_back(index0);
        m_layerIndices[layer].push_back(index1);
        m_layerIndices[layer].push_back(index2);
    }

    for (int layer = 0; layer < LAYER_COUNT; ++layer)
    {
        m_layerIndexCounts[layer] = static_cast<int>(m_layerIndices[layer].size());
    }
}

Terrain::TerrainLayer Terrain::ClassifyTriangle(int index0, int index1, int index2) const
{
    const D3DXVECTOR3& pos0 = m_vertices[index0].position;
    const D3DXVECTOR3& pos1 = m_vertices[index1].position;
    const D3DXVECTOR3& pos2 = m_vertices[index2].position;

    float averageHeight = (pos0.y + pos1.y + pos2.y) / 3.0f;

    D3DXVECTOR3 averageNormal = m_vertices[index0].normal +
        m_vertices[index1].normal + m_vertices[index2].normal;
    D3DXVec3Normalize(&averageNormal, &averageNormal);
    float slopeFactor = averageNormal.y; // 1 = flat, 0 = vertical

    if (averageHeight <= 8.6f)
        return LAYER_WATER;

    if (averageHeight > 28.0f || slopeFactor < 0.4f)
        return LAYER_ROCK;

    if (averageHeight < 12.0f || slopeFactor < 0.75f)
        return LAYER_DIRT;

    return LAYER_GRASS;
}

bool Terrain::CreateLayerIndexBuffers(LPDIRECT3DDEVICE9 device)
{
    for (int layer = 0; layer < LAYER_COUNT; ++layer)
    {
        if (m_layerIndexBuffers[layer])
        {
            m_layerIndexBuffers[layer]->Release();
            m_layerIndexBuffers[layer] = NULL;
        }

        if (m_layerIndices[layer].empty())
        {
            m_layerIndexCounts[layer] = 0;
            continue;
        }

        int indexCount = static_cast<int>(m_layerIndices[layer].size());
        LPDIRECT3DINDEXBUFFER9 buffer = NULL;
        HRESULT hr = device->CreateIndexBuffer(
            indexCount * sizeof(DWORD),
            0,
            D3DFMT_INDEX32,
            D3DPOOL_MANAGED,
            &buffer,
            NULL);

        if (FAILED(hr))
            return false;

        void* pIndices = NULL;
        if (FAILED(buffer->Lock(0, 0, &pIndices, 0)))
        {
            buffer->Release();
            return false;
        }

        std::memcpy(pIndices, m_layerIndices[layer].data(), indexCount * sizeof(DWORD));
        buffer->Unlock();

        m_layerIndexBuffers[layer] = buffer;
        m_layerIndexCounts[layer] = indexCount;
    }

    return true;
}
void Terrain::Render(LPDIRECT3DDEVICE9 device)
{
    if (!device || !m_pVertexBuffer)
        return;

    DWORD prevAlphaBlend = 0;
    DWORD prevAlphaTest = 0;
    DWORD prevLighting = 0;
    device->GetRenderState(D3DRS_ALPHABLENDENABLE, &prevAlphaBlend);
    device->GetRenderState(D3DRS_ALPHATESTENABLE, &prevAlphaTest);
    device->GetRenderState(D3DRS_LIGHTING, &prevLighting);

    device->SetRenderState(D3DRS_ALPHABLENDENABLE, FALSE);
    device->SetRenderState(D3DRS_ALPHATESTENABLE, FALSE);
    device->SetRenderState(D3DRS_LIGHTING, TRUE);

    D3DMATERIAL9 material = {};
    material.Diffuse.r = material.Diffuse.g = material.Diffuse.b = material.Diffuse.a = 1.0f;
    material.Ambient = material.Diffuse;
    material.Specular.r = material.Specular.g = material.Specular.b = 0.0f;
    material.Specular.a = 0.0f;
    device->SetMaterial(&material);

    device->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_MODULATE);
    device->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE);
    device->SetTextureStageState(0, D3DTSS_COLORARG2, D3DTA_DIFFUSE);
    device->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1);
    device->SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE);

    device->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);
    device->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
    device->SetSamplerState(0, D3DSAMP_MIPFILTER, D3DTEXF_NONE);
    device->SetSamplerState(0, D3DSAMP_ADDRESSU, D3DTADDRESS_WRAP);
    device->SetSamplerState(0, D3DSAMP_ADDRESSV, D3DTADDRESS_WRAP);

    D3DXMATRIX matWorld;
    D3DXMatrixIdentity(&matWorld);
    device->SetTransform(D3DTS_WORLD, &matWorld);

    device->SetStreamSource(0, m_pVertexBuffer, 0, sizeof(TerrainVertex));
    device->SetFVF(TerrainVertex::FVF);

    struct LayerInfo
    {
        TerrainLayer layer;
        LPDIRECT3DTEXTURE9 texture;
    };

    const LayerInfo layers[] = {
        { LAYER_GRASS, m_pTextureGrass },
        { LAYER_DIRT,  m_pTextureDirt },
        { LAYER_ROCK,  m_pTextureRock },
        { LAYER_WATER, m_pTextureWater }
    };

    for (const LayerInfo& info : layers)
    {
        if (!info.texture)
            continue;

        int indexCount = m_layerIndexCounts[info.layer];
        if (indexCount <= 0 || !m_layerIndexBuffers[info.layer])
            continue;

        device->SetTexture(0, info.texture);
        device->SetIndices(m_layerIndexBuffers[info.layer]);
        device->DrawIndexedPrimitive(D3DPT_TRIANGLELIST, 0, 0, m_vertexCount, 0, indexCount / 3);
    }

    device->SetRenderState(D3DRS_ALPHABLENDENABLE, prevAlphaBlend);
    device->SetRenderState(D3DRS_ALPHATESTENABLE, prevAlphaTest);
    device->SetRenderState(D3DRS_LIGHTING, prevLighting);
}

float Terrain::GetHeight(float x, float z)
{
    // Interpolate triangle heights so objects sit flush with the terrain
    if (x < 0.0f || z < 0.0f)
        return 0.0f;

    int gridX = static_cast<int>(x);
    int gridZ = static_cast<int>(z);

    if (gridX < 0 || gridX >= m_width - 1 || gridZ < 0 || gridZ >= m_height - 1)
        return 0.0f;

    float h00 = m_heightMap[gridZ * m_width + gridX];
    float h10 = m_heightMap[gridZ * m_width + (gridX + 1)];
    float h01 = m_heightMap[(gridZ + 1) * m_width + gridX];
    float h11 = m_heightMap[(gridZ + 1) * m_width + (gridX + 1)];

    float localX = x - gridX;
    float localZ = z - gridZ;

    if (localX + localZ <= 1.0f)
    {
        return h00 * (1.0f - localX - localZ) + h10 * localX + h01 * localZ;
    }

    float weightA = 1.0f - localZ;        // h10
    float weightB = localX + localZ - 1.0f; // h11
    float weightC = 1.0f - localX;        // h01

    return h10 * weightA + h11 * weightB + h01 * weightC;
}

void Terrain::Cleanup()
{
    if (m_pTextureWater)
    {
        m_pTextureWater->Release();
        m_pTextureWater = NULL;
    }

    if (m_pTextureRock)
    {
        m_pTextureRock->Release();
        m_pTextureRock = NULL;
    }

    if (m_pTextureDirt)
    {
        m_pTextureDirt->Release();
        m_pTextureDirt = NULL;
    }

    if (m_pTextureGrass)
    {
        m_pTextureGrass->Release();
        m_pTextureGrass = NULL;
    }

    for (size_t layer = 0; layer < m_layerIndexBuffers.size(); ++layer)
    {
        if (m_layerIndexBuffers[layer])
        {
            m_layerIndexBuffers[layer]->Release();
            m_layerIndexBuffers[layer] = NULL;
        }
        m_layerIndexCounts[layer] = 0;
    }

    if (m_pIndexBuffer)
    {
        m_pIndexBuffer->Release();
        m_pIndexBuffer = NULL;
    }

    if (m_pVertexBuffer)
    {
        m_pVertexBuffer->Release();
        m_pVertexBuffer = NULL;
    }
}











